package com.md.domain.service;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.md.application.assembler.RoleAssembler;
import com.md.application.assembler.UsersAssembler;
import com.md.base.exception.ServerException;
import com.md.client.OpenApiClient;
import com.md.domain.model.bo.QueryRoleBO;
import com.md.domain.model.bo.QueryUsersBO;
import com.md.domain.model.po.RoleApiPO;
import com.md.domain.model.po.RoleModulePO;
import com.md.domain.model.po.RolePO;
import com.md.domain.model.po.UsersPO;
import com.md.domain.model.po.UsersRolePO;
import com.md.domain.repository.RoleApiRepository;
import com.md.domain.repository.RoleModuleRepository;
import com.md.domain.repository.RoleRepository;
import com.md.domain.repository.UsersRepository;
import com.md.domain.repository.UsersRoleRepository;
import com.md.infrastructure.value.ErrorEnum;
import com.md.model.page.PageResponse;
import com.md.response.ApiResponse;
import com.md.response.AppInfoResponse;
import com.md.response.RoleModuleResponse;
import com.md.response.RoleResponse;
import com.md.response.UsersApiResponse;
import com.md.response.UsersAppInfoResponse;
import com.md.response.UsersDetailResponse;
import com.md.response.UsersModuleResponse;
import com.md.response.UsersResponse;
import com.md.response.open.SimpleUsersResponse;
import com.md.util.ClassUtils;
import com.md.util.CollectionUtil;
import com.md.util.EncryptionUtils;
import com.md.util.ObjectUtil;
import com.md.util.PageUtil;
import com.md.util.ThreadUtil;
import com.md.util.system.Snowflake;
import com.md.value.RoleApiTypeEnum;
import com.md.value.RoleModuleTypeEnum;
import com.md.value.RoleStatusEnum;
import com.md.value.server.ServerContext;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

/**
 * @author 刘骄阳
 * 2024-12-15 1:47
 */
@Service
@RequiredArgsConstructor
public class AuthenticationDomainServices {
  private final UsersRepository usersRepository;
  private final RoleRepository roleRepository;
  private final UsersRoleRepository usersRoleRepository;
  private final RoleModuleRepository roleModuleRepository;
  private final RoleApiRepository roleApiRepository;
  private final Snowflake snowflake;

  public void createUser(final UsersPO users, final List<Long> roleIdList) {
    if (ObjectUtil.isEmpty(users.getAccount())) {
      users.setAccount(String.valueOf(snowflake.nextId()));
    }
    users.setPassword(EncryptionUtils.md5Encryption(users.getPassword(), users.getAccount()));
    usersRepository.insert(users);
    linkUsersRole(users.getId(), roleIdList);
  }

  public void checkUserFormat(final String phone, final String name) {
    UsersPO users = usersRepository.queryUserByPhone(phone);
    if (ObjectUtil.notEmpty(users)) {
      throw ServerException.build(ErrorEnum.MESSAGE_IS_EXISTS, "手机号");
    }
    users = usersRepository.queryUserByName(name);
    if (ObjectUtil.notEmpty(users)) {
      throw ServerException.build(ErrorEnum.MESSAGE_IS_EXISTS, "用户名");
    }
  }

  public void createRole(final RolePO role) {
    String name = role.getName();
    RolePO rolePO = roleRepository.queryRoleByName(name);
    if (ObjectUtil.notEmpty(rolePO)) {
      throw ServerException.build(ErrorEnum.MESSAGE_IS_EXISTS, "角色名");
    }
    Optional.ofNullable(role.getParentId()).map(roleRepository::selectById).ifPresent(parentRole -> {
      role.setLevel(parentRole.getLevel() + 1);
      String roleLink;
      if (ObjectUtil.isEmpty(parentRole.getRoleLink())) {
        roleLink = parentRole.getId().toString() + ",";
      } else {
        roleLink = parentRole.getRoleLink() + parentRole.getId().toString() + ",";
      }
      role.setRoleLink(roleLink);
    });
    role.setStatus(RoleStatusEnum.DISABLED.getCode());
    roleRepository.insert(role);
    ThreadUtil.runAsync(() -> createRoleAfter(role.getParentId(), role.getId()), "创建角色后置处理");
  }

  public void createRoleAfter(final Long parentRoleId, final Long roleId) {
    if (ObjectUtil.isEmpty(parentRoleId)) {
      roleRepository.updateRoleStatus(roleId, RoleStatusEnum.NORMAL);
      return;
    }
    RoleModulePO queryRoleModulePO = new RoleModulePO();
    queryRoleModulePO.setRoleId(parentRoleId);
    List<Long> addModuleIdList =
      CollectionUtil.map(roleModuleRepository.selectList(queryRoleModulePO), RoleModulePO::getModuleId);
    linkRoleModule(roleId, addModuleIdList, null, 0, RoleModuleTypeEnum.EXTENDS_PARENT);
    RoleApiPO queryApiPO = new RoleApiPO();
    queryApiPO.setRoleId(parentRoleId);
    Map<Long, List<RoleApiPO>> roleApiMapGroupByModuleId =
      CollectionUtil.groupBy(roleApiRepository.selectList(queryApiPO), RoleApiPO::getModuleId);
    roleApiMapGroupByModuleId.forEach((moduleId, roleApiPOS) -> {
      List<Long> addApiIdList = CollectionUtil.map(roleApiPOS, RoleApiPO::getApiId);
      // 继承父级的权限
      linkRoleApi(roleId, addApiIdList, null, RoleApiTypeEnum.EXTENDS_PARENT, moduleId);
    });
    roleRepository.updateRoleStatus(roleId, RoleStatusEnum.NORMAL);
  }

  public void updateRole(final RolePO role) {
    String name = role.getName();
    RolePO rolePO = roleRepository.queryRoleByName(name);
    if (ObjectUtil.notEmpty(rolePO)) {
      throw ServerException.build(ErrorEnum.MESSAGE_IS_EXISTS, "角色名");
    }
    RolePO updateRole = ClassUtils.copyProperty(role, RolePO::getId, RolePO::getName, RolePO::getFirstLabel);
    roleRepository.update(updateRole);
  }

  public void linkUsersRole(final Long userId, final List<Long> roleIdList) {
    if (ObjectUtil.isEmpty(roleIdList)) {
      return;
    }
    List<Long> dbRoleList = CollectionUtil.map(usersRoleRepository.selectUserRole(userId), UsersRolePO::getRoleId);
    roleIdList.removeAll(dbRoleList);
    List<UsersRolePO> usersRoleList = Lists.newArrayList();
    roleIdList.forEach(roleId -> {
      UsersRolePO usersRolePO = new UsersRolePO();
      usersRolePO.setUserId(userId);
      usersRolePO.setRoleId(roleId);
      usersRoleList.add(usersRolePO);
    });
    if (ObjectUtil.notEmpty(usersRoleList)) {
      usersRoleRepository.insertBatch(usersRoleList);
    }
  }

  public void linkRoleApi(final Long roleId,
                          final List<Long> addApiIdList,
                          final List<Long> delApiIdList,
                          final RoleApiTypeEnum roleApiType,
                          final Long moduleId) {
    if (ObjectUtil.isAllEmpty(addApiIdList, delApiIdList)) {
      return;
    }
    addRoleApiLink(roleId, addApiIdList, roleApiType, moduleId);
    delRoleApiLink(roleId, delApiIdList, moduleId);
  }

  private void addRoleApiLink(final Long roleId, final List<Long> addApiIdList, final RoleApiTypeEnum roleApiType,
                              final Long moduleId) {
    RoleApiPO selectPO = new RoleApiPO();
    selectPO.setRoleId(roleId);
    Map<Long, RoleApiPO> dbRoleApiMap =
      CollectionUtil.toMap(roleApiRepository.selectList(selectPO), RoleApiPO::getApiId, Function.identity());
    List<RoleApiPO> addRoleApiList = Lists.newArrayList();
    List<Long> updateRoleApiIdList = Lists.newArrayList();
    addApiIdList.forEach(apiId -> {
      RoleApiPO dbRoleApiPO = dbRoleApiMap.get(apiId);
      if (ObjectUtil.isEmpty(dbRoleApiPO)) {
        RoleApiPO roleApiPO = new RoleApiPO();
        roleApiPO.setApiId(apiId);
        roleApiPO.setRoleId(roleId);
        roleApiPO.setModuleId(moduleId);
        roleApiPO.setType(roleApiType.getCode());
        addRoleApiList.add(roleApiPO);
      } else if (ObjectUtil.unEquals(dbRoleApiPO.getType(), roleApiType.getCode())) {
        updateRoleApiIdList.add(dbRoleApiPO.getId());
      }
    });
    if (ObjectUtil.notEmpty(addRoleApiList)) {
      roleApiRepository.insertBatch(addRoleApiList);
    }
    if (ObjectUtil.notEmpty(addRoleApiList)) {
      roleApiRepository.updateTypeByIdList(updateRoleApiIdList, roleApiType.getCode());
    }
  }

  private void delRoleApiLink(final Long roleId, final List<Long> delApiIdList, final Long moduleId) {
    if (ObjectUtil.isEmpty(delApiIdList)) {
      return;
    }
    List<Long> moduleIdList = Lists.newArrayList(moduleId);
    List<RolePO> childrenList = selectChildrenList(roleId);
    List<Long> childrenRoleIdList = CollectionUtil.map(childrenList, RolePO::getId);
    childrenRoleIdList.add(roleId);
    roleApiRepository.delRoleApiLink(childrenRoleIdList, delApiIdList, moduleIdList);
  }

  public List<RolePO> selectChildrenList(final Long roleId) {
    RolePO rolePO = roleRepository.selectById(roleId);
    String roleLink;
    if (ObjectUtil.isEmpty(rolePO.getRoleLink())) {
      roleLink = roleId + ",";
    } else {
      roleLink = rolePO.getRoleLink() + roleId.toString() + ",";
    }
    return roleRepository.selectChildrenRoleList(roleLink);
  }

  public UsersResponse queryUserInfo(final Long id) {
    UsersPO usersPO = usersRepository.selectById(id);
    if (ObjectUtil.isEmpty(usersPO)) {
      throw ServerException.build(ErrorEnum.USERS_NOT_EXIST);
    }
    return UsersAssembler.buildUsers2Response(usersPO);
  }

  public RoleResponse queryRoleInfo(final Long id) {
    RolePO rolePO = roleRepository.selectById(id);
    if (ObjectUtil.isEmpty(rolePO)) {
      throw ServerException.build(ErrorEnum.ROLE_NOT_EXIST);
    }
    List<RoleModuleResponse> rolePermission = queryRolePermission(id);
    return RoleAssembler.buildRole2Response(rolePO, rolePermission);
  }

  public Boolean checkPermission(final Long userId, final Long apiId) {
    Integer permissionCount = roleApiRepository.checkPermission(userId, apiId);
    return permissionCount > 0;
  }

  public Boolean checkModulePermission(final Long userId, final List<Long> moduleIdList) {
    if (ObjectUtil.isEmpty(moduleIdList)) {
      return true;
    }
    Set<Long> dbUserHasModulePermissionList =
      CollectionUtil.mapSet(roleModuleRepository.selectUserModulePermission(userId), RoleModulePO::getModuleId);
    if (ObjectUtil.isEmpty(dbUserHasModulePermissionList)) {
      return false;
    }
    return dbUserHasModulePermissionList.containsAll(moduleIdList);
  }

  public void linkRoleModule(final Long roleId, final List<Long> addModuleIdList, final List<Long> delModuleIdList,
                             final Integer isOwner, final RoleModuleTypeEnum type) {
    if (ObjectUtil.isAllEmpty(addModuleIdList, delModuleIdList)) {
      return;
    }
    addRoleModuleLink(roleId, addModuleIdList, isOwner, type);
    delRoleModuleLink(roleId, delModuleIdList);
  }

  private void addRoleModuleLink(final Long roleId, final List<Long> addModuleIdList, final Integer isOwner,
                                 final RoleModuleTypeEnum type) {
    RoleModulePO selectPO = new RoleModulePO();
    selectPO.setRoleId(roleId);
    Map<Long, RoleModulePO> dbModuleMap = CollectionUtil.toMap(roleModuleRepository.selectList(selectPO),
      RoleModulePO::getModuleId, Function.identity());
    List<RoleModulePO> addRoleModuleList = Lists.newArrayList();
    List<Long> updateRoleModuleList = Lists.newArrayList();
    addModuleIdList.forEach(moduleId -> {
      RoleModulePO dbRoleModulePO = dbModuleMap.get(moduleId);
      if (ObjectUtil.isEmpty(dbRoleModulePO)) {
        RoleModulePO roleModulePO = new RoleModulePO();
        roleModulePO.setModuleId(moduleId);
        roleModulePO.setRoleId(roleId);
        roleModulePO.setIsOwner(isOwner);
        roleModulePO.setType(type.getCode());
        addRoleModuleList.add(roleModulePO);
      } else if (ObjectUtil.unEquals(dbRoleModulePO.getType(), type.getCode())) {
        updateRoleModuleList.add(dbRoleModulePO.getId());
      }
    });
    if (ObjectUtil.notEmpty(addRoleModuleList)) {
      roleModuleRepository.insertBatch(addRoleModuleList);
    }
    if (ObjectUtil.notEmpty(updateRoleModuleList)) {
      roleModuleRepository.updateTypeByIdList(updateRoleModuleList, type.getCode());
    }
  }

  private void delRoleModuleLink(final Long roleId, final List<Long> delModuleIdList) {
    List<RolePO> childrenList = selectChildrenList(roleId);
    List<Long> childrenRoleIdList = CollectionUtil.map(childrenList, RolePO::getId);
    childrenRoleIdList.add(roleId);
    // 删除模块
    roleModuleRepository.delRoleModuleLink(childrenRoleIdList, delModuleIdList);
    // 删除api
    roleApiRepository.delRoleApiLink(childrenRoleIdList, null, delModuleIdList);
  }

  public PageResponse<UsersModuleResponse> moduleList(final Long userId, final Long pageNum, final Long pageSize) {
    List<Long> moduleIdList = CollectionUtil.map(roleModuleRepository.selectUserModulePermission(userId),
      RoleModulePO::getModuleId);
    long total = moduleIdList.size();
    moduleIdList = PageUtil.limitList(moduleIdList, pageNum, pageSize);
    List<UsersModuleResponse> moduleResponses =
      ClassUtils.source2Target(OpenApiClient.openQueryListByIdList(moduleIdList), UsersModuleResponse.class);
    return PageUtil.buildPageResponse(moduleResponses, total, pageNum, pageSize);
  }

  public PageResponse<UsersAppInfoResponse> appInfoList(final Long moduleId, final Long pageNum, final Long pageSize) {
    if (roleModuleRepository.checkPermission(ServerContext.getUserId(), moduleId) == 0) {
      throw ServerException.build(com.md.value.enums.ErrorEnum.PERMISSION);
    }
    List<AppInfoResponse> appInfoResponses = OpenApiClient.queryAppInfoByModuleId(moduleId);
    long total = appInfoResponses.size();
    appInfoResponses = PageUtil.limitList(appInfoResponses, pageNum, pageSize);
    List<UsersAppInfoResponse> usersAppInfoResponses =
      ClassUtils.source2Target(appInfoResponses, UsersAppInfoResponse.class);
    return PageUtil.buildPageResponse(usersAppInfoResponses, total, pageNum, pageSize);
  }

  public PageResponse<UsersApiResponse> apiList(final Long appInfoId, final Long pageNum, final Long pageSize) {
    List<ApiResponse> apiResponses = OpenApiClient.queryApi(appInfoId);
    long total = apiResponses.size();
    apiResponses = PageUtil.limitList(apiResponses, pageNum, pageSize);
    List<UsersApiResponse> usersAppInfoResponses = ClassUtils.source2Target(apiResponses, UsersApiResponse.class);
    return PageUtil.buildPageResponse(usersAppInfoResponses, total, pageNum, pageSize);
  }

  public PageResponse<RoleResponse> queryRoleList(final QueryRoleBO queryRoleBO) {
    Page<RolePO> rolePOPage = roleRepository.selectListByPage(queryRoleBO.getPageNum(), queryRoleBO.getPageSize(),
      queryRoleBO.getOrderColum(), queryRoleBO.getName(), queryRoleBO.getStatus());
    return PageUtil.buildPageResponse(rolePOPage, rolePO -> RoleAssembler.buildRole2Response(rolePO, null));
  }

  public PageResponse<UsersResponse> list(final QueryUsersBO queryUsersBO) {
    Page<UsersPO> usersPage = usersRepository.selectListByPage(queryUsersBO);
    return PageUtil.buildPageResponse(usersPage, UsersAssembler::buildUsers2Response);
  }

  public List<RoleModuleResponse> queryRolePermission(final Long roleId) {
    List<Long> moduleIdList =
      CollectionUtil.map(roleModuleRepository.selectRoleModule(roleId), RoleModulePO::getModuleId);
    List<RoleApiPO> roleApiList = roleApiRepository.selectRoleApi(roleId);
    Map<Long, List<Long>> groupByModuleId =
      CollectionUtil.groupBy(roleApiList, RoleApiPO::getModuleId, RoleApiPO::getApiId);
    List<RoleModuleResponse> responseList = Lists.newArrayList();

    moduleIdList.forEach(moduleId -> {
      RoleModuleResponse response = new RoleModuleResponse();
      response.setModuleId(moduleId);
      response.setApiIdList(groupByModuleId.getOrDefault(moduleId, Lists.newArrayList()));
      responseList.add(response);
    });
    return responseList;
  }

  /**
   * 查询用户详情
   *
   * @param id 用户id
   * @return 用户详情
   */
  public UsersDetailResponse usersDetail(final Long id) {
    // 查询用户详情，查询角色列表
    UsersPO users = usersRepository.selectById(id);
    if (ObjectUtil.isEmpty(users)) {
      throw ServerException.build(ErrorEnum.USERS_NOT_EXIST);
    }
    List<Long> usersRoleIdList = CollectionUtil.map(usersRoleRepository.selectUserRole(id), UsersRolePO::getRoleId);
    if (ObjectUtil.isEmpty(usersRoleIdList)) {
      return UsersAssembler.buildUsers2DetailResponse(users, Lists.newArrayList());
    }
    List<RolePO> roleList = roleRepository.selectByIds(usersRoleIdList);
    Long userFirstRoleId = roleList.get(0).getId();
    List<RoleResponse> userRoleResponseList = CollectionUtil.map(roleList, rolePO -> {
      if (ObjectUtil.equals(userFirstRoleId, rolePO.getId())) {
        return RoleAssembler.buildRole2Response(rolePO, queryRolePermission(userFirstRoleId));
      }
      return RoleAssembler.buildRole2Response(rolePO, Lists.newArrayList());
    });
    return UsersAssembler.buildUsers2DetailResponse(users, userRoleResponseList);
  }

  public SimpleUsersResponse simpleInfo(final String phone, final String account) {
    if (ObjectUtil.notEmpty(phone)) {
      return UsersAssembler.buildSimpleUsersResponse(usersRepository.queryUserByPhone(phone));
    }
    if (ObjectUtil.notEmpty(account)) {
      return UsersAssembler.buildSimpleUsersResponse(usersRepository.queryUserByAccount(account));
    }
    return null;
  }
}

